/*
 * GuildFraktionInfo - list the reputation and the guild level of all members of a wow guild
 * Copyright (C) [2012 - 2013] [www.marantis.de]
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later 
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not, 
 * see <http://www.gnu.org/licenses/>.
 */
package de.marantis.app.wow.gfi.helper;

import de.marantis.app.wow.gfi.Main;
import de.marantis.app.wow.gfi.beans.ConfigBean;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class ConfigHelper {

    private final static Logger log = Logger.getLogger(ConfigHelper.class);
    public final static String bnetDomain = "battle.net";
    
    public final static int GUILD_LEVEL_BASE = 0;
    public final static int REPUTATION_LEVEL_BASE = 0;
    public final static int REPUTATION_LEVEL_MAX = 7;

    public static void printHelp() {

        StringBuffer sb = new StringBuffer("");
        sb.append("*** Guild Fraction Information (GFI " + Main.version + ") ***\n");
        sb.append("\n");
        sb.append("Parameters:\n");
        sb.append("-f: = the config file\n");

        System.out.println(sb.toString());
    }

    public static boolean checkArgs(String[] args, ConfigBean config) {

        if (config == null) {
            config = new ConfigBean();
        }

        if (args == null) {
            return false;
        }

        if (args.length <= 0) {
            return false;
        }

        for (String arg : args) {

            if (arg == null) {
                continue;
            }

            arg = arg.trim();
            if (arg.length() <= 0) {
                continue;
            }

            //*** get the config file ****
            if (arg.startsWith("-f:")) {
                String param = arg.substring(3, arg.length());
                boolean okFlag = setupGfiConfigFile(config, param);
                if (!okFlag) {
                    return false;
                } else {
                    break;
                }
            }
        }

        //*** read/check the params from the property file ***
        Properties p = config.getGfiConfigProperties();
        if (p != null) {
            boolean okFlag = readConfigProperties(config, p);
            if (!okFlag) {
                return false;
            }
        }

        return true;
    }

    public static boolean setupGfiConfigFile(ConfigBean config, String fileString) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            config.setGfiConfigFile(null);
            config.setGfiConfigProperties(null);

            //*** get the File object ***
            File f = new File(fileString);
            config.setGfiConfigFile(f);

            log.info("GFI config file: " + f.toString());

            if (!f.exists()) {
                log.error("GFI config file doesn't exist!");
                return false;
            }

            if (!f.isFile()) {
                log.error("GFI config file is not a file!!");
                return false;
            }

            if (!f.canRead()) {
                log.error("GFI config file is read protected!");
                return false;
            }

            //*** read the properties (UTF-8) ***
            Reader in = new InputStreamReader(new FileInputStream(f), "UTF-8");
            Properties p = new Properties();
            p.load(in);
            
            config.setGfiConfigProperties(p);
            return true;

        } catch (Exception e) {
            log.error("Init GFI config file exception!", e);
            return false;
        }
    }

    private static boolean readConfigProperties(ConfigBean config, Properties p) {
        try {
            boolean okFlag = false;

            String log4jConfigFile = (String) p.get(ConfigPropertiesEnum.log4jConfigFile.toString());
            okFlag = setupLog4JConfigFile(config, log4jConfigFile);
            if (!okFlag) {
                return false;
            }

            Log4JPostPrint(config);
            if (!okFlag) {
                return false;
            }

            String consoleMode = (String) p.get(ConfigPropertiesEnum.consoleMode.toString());
            setupConsoleMode(config, consoleMode);
            
            String testMode = (String) p.get(ConfigPropertiesEnum.testMode.toString());
            setupTestMode(config, testMode);
            
            String armoryUrl = (String) p.get(ConfigPropertiesEnum.armoryUrl.toString());
            okFlag = setupArmoryUrl(config, armoryUrl);
            if (!okFlag) {
                return false;
            }

            String jsonExportFile = (String) p.get(ConfigPropertiesEnum.jsonExportFile.toString());
            okFlag = setupJsonExportFile(config, jsonExportFile);
            if (!okFlag) {
                return false;
            }

            okFlag = setupGuildLevelName(config, p);
            if (!okFlag) {
                return false;
            }

            okFlag = setupGuildLevelFilter(config, p);
            if (!okFlag) {
                return false;
            }

            okFlag = setupGuildReputationFilter(config, p);
            if (!okFlag) {
                return false;
            }

            return true;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }

    public static boolean setupConsoleMode(ConfigBean config, String consoleModeString) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            config.setConsoleMode(false);

            consoleModeString = CommonHelper.Trim2Null(consoleModeString);
            if (consoleModeString == null) {
                return false;
            }

            //*** remove the CGI options (if there are any) ***
            boolean b = CommonHelper.String2Boolean(consoleModeString);
            config.setConsoleMode(b);
            return b;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }
    
    public static boolean setupTestMode(ConfigBean config, String testModeString) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            config.setTestMode(false);

            testModeString = CommonHelper.Trim2Null(testModeString);
            if (testModeString == null) {
                return false;
            }

            //*** remove the CGI options (if there are any) ***
            boolean b = CommonHelper.String2Boolean(testModeString);
            config.setTestMode(b);
            return b;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }
    
    public static boolean setupArmoryUrl(ConfigBean config, String urlString) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            config.setArmoryUrl(null);

            urlString = CommonHelper.Trim2Null(urlString);
            if (urlString == null) {
                return false;
            }

            //*** remove the CGI options (if there are any) ***
            String link = CommonHelper.getUrlWithoutCGI(urlString);
            if (link == null) {
                return false;
            }

            //*** get the URL object ***
            URL u = new URL(link);

            //*** check if it's a battle.net domain ***
            String host = u.getHost();
            if (!host.endsWith(ConfigHelper.bnetDomain)) {
                throw new IllegalArgumentException(u.toString() + " it's not a battle.net domain!");
            }

            log.info("Armory url: " + u.toString());
            config.setArmoryUrl(u);
            return true;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }

    public static boolean setupJsonExportFile(ConfigBean config, String fileString) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            config.setJsonExportFile(null);

            fileString = CommonHelper.Trim2Null(fileString);
            if (fileString == null) {
                return false;
            }

            File f = new File(fileString);
            log.info("Json file: " + f.toString());
            config.setJsonExportFile(f);
            return true;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }

    public static boolean setupLog4JConfigFile(ConfigBean config, String fileString) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            config.setLog4jConfigFile(null);

            fileString = CommonHelper.Trim2Null(fileString);
            if (fileString == null) {
                return false;
            }

            File f = new File(fileString);
            log.info("Log4J config file: " + f.toString());
            config.setLog4jConfigFile(f);

            Reader in = new InputStreamReader(new FileInputStream(f), "UTF-8");
            Properties p = new Properties();
            p.load(in);
            PropertyConfigurator.configure(p);

            return true;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }

    private static boolean setupGuildLevelName(ConfigBean config, Properties p) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            ArrayList<String> levelNames = new ArrayList<String>();
            config.setGuildLevelNames(levelNames);

            int idx = GUILD_LEVEL_BASE;
            while (true) {
                String varName = ConfigPropertiesEnum.guildLevelName.toString() + String.valueOf(idx);
                String varValue = (String) p.get(varName);
                varValue = CommonHelper.Trim2Null(varValue);
                if (varValue == null) {
                    break;
                }

                idx += 1;
                levelNames.add(varValue);
            }

            config.setGuildLevelCount(idx);
            return true;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }

    private static boolean setupGuildLevelFilter(ConfigBean config, Properties p) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            HashMap<Integer, Integer> levelFilter = new HashMap<Integer, Integer>();
            config.setGuildLevelFilter(levelFilter);

            int guildLevelCount = config.getGuildLevelCount();
            for (int idx = GUILD_LEVEL_BASE; idx < guildLevelCount; idx++) {
                String varName = ConfigPropertiesEnum.guildLevelFilter.toString() + String.valueOf(idx);
                String varValue = (String) p.get(varName);
                varValue = CommonHelper.Trim2Null(varValue);
                if (varValue == null) {
                    continue;
                }

                Integer intValue = CommonHelper.String2Int(varValue);
                if (intValue < 0 || intValue > 8) {
                    continue;
                }

                levelFilter.put(idx, intValue);
            }

            return true;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }

    private static boolean setupGuildReputationFilter(ConfigBean config, Properties p) {
        try {
            if (config == null) {
                config = new ConfigBean();
            }

            HashMap<Integer, Integer> reputationFilter = new HashMap<Integer, Integer>();
            config.setGuildReputationFilter(reputationFilter);

            int guildLevelCount = config.getGuildLevelCount();

            for (int idx = REPUTATION_LEVEL_BASE; idx <= REPUTATION_LEVEL_MAX; idx++) {
                String varName = ConfigPropertiesEnum.guildReputationFilter.toString() + String.valueOf(idx);
                String varValue = (String) p.get(varName);
                varValue = CommonHelper.Trim2Null(varValue);
                if (varValue == null) {
                    continue;
                }

                Integer intValue = CommonHelper.String2Int(varValue);
                if (intValue < 0 || intValue > (guildLevelCount - 1)) {
                    continue;
                }

                reputationFilter.put(idx, intValue);
            }

            return true;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }


    /*
     * Until this function will be called all log writings has been printed just
     * with the minimal Log4J config to the console.
     * 
     * This function write the log infos AGAIN but with the "final" Log4J config 
     * (ex to a log file, too).
     */
    private static boolean Log4JPostPrint(ConfigBean config) {
        try {
            log.info("GFI config file: " + config.getGfiConfigFile().toString());
            log.info("Log4J config file: " + config.getLog4jConfigFile().toString());
            
            if (log.isDebugEnabled()){
                log.info("GFI config properties: " + config.getGfiConfigProperties().toString());
            }
            
            return true;

        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }
}
